// (c) Copyright 2013-2014 Stephen Gold <sgold@sonic.net>
// Distributed under the terms of the GNU General Public License

/*
 This file is part of the Bats Game.

 The Bats Game is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by the
 Free Software Foundation, either version 3 of the License, or (at your
 option) any later version.

 The Bats Game is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.

 You should have received a copy of the GNU General Public License
 along with the Bats Game.  If not, see <http://www.gnu.org/licenses/>.
 */
package bats.scene;

import com.jme3.scene.Node;
import com.jme3.scene.SceneGraphVisitor;
import com.jme3.scene.Spatial;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A scene control whose spatial must be a Node.
 *
 * @author Stephen Gold <sgold@sonic.net>
 */
public class NodeControl
        extends SceneControl {
    // *************************************************************************
    // constants

    /**
     * message logger for this class
     */
    final private static Logger logger =
            Logger.getLogger(NodeControl.class.getName());
    // *************************************************************************
    // constructors

    /**
     * Instantiate an enabled control for a specified scene.
     *
     * @param scene which scene contains the control (not null)
     */
    protected NodeControl(SceneNode scene) {
        super(scene);
        assert isEnabled();
    }
    // *************************************************************************
    // new methods exposed

    /**
     * Add a spatial as a child of the node.
     *
     * @param child which spatial to add (not null)
     */
    public void attach(Spatial child) {
        assert child != null;
        getNode().attachChild(child);
    }

    /**
     * Count how many child spatials the node has.
     *
     * @return count (&ge;0)
     */
    public int countChildren() {
        int count = getChildren().size();
        assert count >= 0 : count;
        return count;
    }

    /**
     * Detach a child spatial from the node.
     *
     * @param child which spatial to detach (not null)
     */
    public void detach(Spatial child) {
        assert scene.contains(child);

        int position = getNode().detachChild(child);
        assert position >= 0 : position;
    }

    /**
     * Delete all of the node's child spatials.
     */
    public void deleteAll() {
        for (Spatial child : getChildren()) {
            scene.deleteSpatial(child);
        }
        assert countChildren() == 0 : countChildren();
    }

    /**
     * Access the node's list of child spatials.
     *
     * @return the pre-existing instance (not null)
     */
    protected List<Spatial> getChildren() {
        List<Spatial> children = getNode().getChildren();
        assert children != null;
        return children;
    }

    /**
     * Access the controlled node.
     *
     * @return the pre-existing instance (not null)
     */
    protected Node getNode() {
        Node node = (Node) spatial;
        assert node != null;
        return node;
    }

    /**
     * Access the node's first and only child spatial.
     *
     * @return the pre-existing instance (or null if number of children != 1)
     */
    public Spatial getOnlyChild() {
        List<Spatial> children = getChildren();
        if (children.size() != 1) {
            return null;
        }
        Spatial child = children.get(0);
        return child;
    }

    /**
     * Traverse the node's subtree in depth-first order.
     *
     * @param visitor method invoked on each spatial (not null)
     */
    public void traverse(SceneGraphVisitor visitor) {
        assert visitor != null;

        spatial.depthFirstTraversal(visitor);
    }
    // *************************************************************************
    // AbstractControl methods

    /**
     * When setting the control's spatial, verify that the new spatial is a node
     * or null -- not a geometry.
     *
     * @param newSpatial (may be null)
     */
    @Override
    public void setSpatial(Spatial newSpatial) {
        if (newSpatial != null && !(newSpatial instanceof Node)) {
            logger.log(Level.SEVERE, "spatial class={0}",
                    newSpatial.getClass());
            throw new IllegalArgumentException(
                    "spatial should be a Node or null");
        }
        super.setSpatial(newSpatial);
    }
}